/*=====================================================
Critical Links
Auto Configuration Server

Authors:
	Rui Eduardo Gouveia Gil - r-gil@critical-links.com

Date: 2010
Version 0.1
=======================================================*/
#ifndef __HTTPJAVASCRIPTINTERFACE_H__
#define __HTTPJAVASCRIPTINTERFACE_H__

#include <map>
#include <fstream>
#include <sstream>
#include <iostream>
#include <rgcpp/structs/Set.h>
#include <rgcpp/utils/LogHandler.h>
#include <rgcpp/exceptions/RuntimeException.h>

#include "ACSConfig.h"

namespace acs
{

template<typename Data>
class Deserializer
{
	public:
		static Data deserialize(const std::string& value);
};

template<>
class Deserializer<char>
{
	public:
		static char deserialize(const std::string& value)
		{
			return atoi(value.c_str());
		}
};

template<>
class Deserializer<bool>
{
	public:
		static bool deserialize(const std::string& value)
		{
			if(value.size() == 1)
				return atoi(value.c_str());
			else if(value.compare("true") == 0)
				return true;
			else
				return false;
		}
};

template<>
class Deserializer<short>
{
	public:
		static int deserialize(const std::string& value)
		{
			return atoi(value.c_str());
		}
};

template<>
class Deserializer<int>
{
	public:
		static int deserialize(const std::string& value)
		{
			return atoi(value.c_str());
		}
};

template<>
class Deserializer<float>
{
	public:
		static float deserialize(const std::string& value)
		{
			return atol(value.c_str());
		}
};

template<>
class Deserializer<double>
{
	public:
		static double deserialize(const std::string& value)
		{
			return atol(value.c_str());
		}
};

template<>
class Deserializer<std::string>
{
	public:
		static std::string deserialize(const std::string& value)
		{
			return value;
		}
};

template<>
class Deserializer<std::vector<char> >
{
	public:
		static std::vector<char> deserialize(const std::string& value)
		{
			std::vector<char> result;

			std::string aux;
			unsigned int pos;
			std::string temp = value;
			while((pos = temp.find(',')) != std::string::npos)
			{
				result.push_back(atoi(temp.substr(0,pos).c_str()));
				temp = temp.substr(pos+1);
			}

			if(!value.empty())
				result.push_back(atoi(temp.c_str()));

			return result;
		}
};

template<>
class Deserializer<std::vector<bool> >
{
	public:
		static std::vector<bool> deserialize(const std::string& value)
		{
			std::vector<bool> result;

			std::string aux;
			unsigned int pos;
			std::string temp = value;
			while((pos = temp.find(',')) != std::string::npos)
			{
				result.push_back(atoi(temp.substr(0,pos).c_str()));
				temp = temp.substr(pos+1);
			}

			if(!value.empty())
				result.push_back(atoi(temp.c_str()));

			return result;
		}
};

template<>
class Deserializer<std::vector<short> >
{
	public:
		static std::vector<short> deserialize(const std::string& value)
		{
			std::vector<short> result;

			unsigned int pos;
			std::string temp = value;
			while((pos = temp.find(',')) != std::string::npos)
			{
				result.push_back(atoi(temp.substr(0,pos).c_str()));
				temp = temp.substr(pos+1);
			}

			if(!value.empty())
				result.push_back(atoi(temp.c_str()));

			return result;
		}
};

template<>
class Deserializer<std::vector<int> >
{
	public:
		static std::vector<int> deserialize(const std::string& value)
		{
			std::vector<int> result;

			unsigned int pos;
			std::string temp = value;
			while((pos = temp.find(',')) != std::string::npos)
			{
				result.push_back(atoi(temp.substr(0,pos).c_str()));
				temp = temp.substr(pos+1);
			}

			if(!value.empty())
				result.push_back(atoi(temp.c_str()));

			return result;
		}
};

template<>
class Deserializer<std::vector<float> >
{
	public:
		static std::vector<float> deserialize(const std::string& value)
		{
			std::vector<float> result;

			unsigned int pos;
			std::string temp = value;
			while((pos = temp.find(',')) != std::string::npos)
			{
				result.push_back(atof(temp.substr(0,pos).c_str()));
				temp = temp.substr(pos+1);
			}

			if(!value.empty())
				result.push_back(atof(temp.c_str()));

			return result;
		}
};

template<>
class Deserializer<std::vector<double> >
{
	public:
		static std::vector<double> deserialize(const std::string& value)
		{
			std::vector<double> result;

			unsigned int pos;
			std::string temp = value;
			while((pos = temp.find(',')) != std::string::npos)
			{
				result.push_back(atof(temp.substr(0,pos).c_str()));
				temp = temp.substr(pos+1);
			}

			if(!value.empty())
				result.push_back(atof(temp.c_str()));

			return result;
		}
};

template<>
class Deserializer<std::vector<std::string> >
{
	public:
		static std::vector<std::string> deserialize(const std::string& value)
		{
			std::vector<std::string> result;

			unsigned int pos;
			std::string temp = value;
			while((pos = temp.find(',')) != std::string::npos)
			{
				result.push_back(temp.substr(0,pos));
				temp = temp.substr(pos+1);
			}

			if(!value.empty())
				result.push_back(temp);

			return result;
		}
};

class Executor
{
	public:
		template<typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10>
		static std::vector<std::string> execute(std::vector<std::string> value, std::vector<std::string> (*func)(A1,A2,A3,A4,A5,A6,A7,A8,A9,A10))
		{
			if(value.size() != 10)
			{
				Throw(rgcpp::IllegalArgumentException("Invalid number of arguments"));
			}

			A1 a1 = Deserializer<A1>::deserialize(value[0]);
			A2 a2 = Deserializer<A2>::deserialize(value[1]);
			A3 a3 = Deserializer<A3>::deserialize(value[2]);
			A4 a4 = Deserializer<A4>::deserialize(value[3]);
			A5 a5 = Deserializer<A5>::deserialize(value[4]);
			A6 a6 = Deserializer<A6>::deserialize(value[5]);
			A7 a7 = Deserializer<A7>::deserialize(value[6]);
			A8 a8 = Deserializer<A8>::deserialize(value[7]);
			A9 a9 = Deserializer<A9>::deserialize(value[8]);
			A10 a10 = Deserializer<A10>::deserialize(value[9]);

			return (*func)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10);
		}

		template<typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9>
		static std::vector<std::string> execute(std::vector<std::string> value, std::vector<std::string> (*func)(A1,A2,A3,A4,A5,A6,A7,A8,A9))
		{
			if(value.size() != 9)
			{
				Throw(rgcpp::IllegalArgumentException("Invalid number of arguments"));
			}

			A1 a1 = Deserializer<A1>::deserialize(value[0]);
			A2 a2 = Deserializer<A2>::deserialize(value[1]);
			A3 a3 = Deserializer<A3>::deserialize(value[2]);
			A4 a4 = Deserializer<A4>::deserialize(value[3]);
			A5 a5 = Deserializer<A5>::deserialize(value[4]);
			A6 a6 = Deserializer<A6>::deserialize(value[5]);
			A7 a7 = Deserializer<A7>::deserialize(value[6]);
			A8 a8 = Deserializer<A8>::deserialize(value[7]);
			A9 a9 = Deserializer<A9>::deserialize(value[8]);

			return (*func)(a1,a2,a3,a4,a5,a6,a7,a8,a9);
		}

		template<typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
		static std::vector<std::string> execute(std::vector<std::string> value, std::vector<std::string> (*func)(A1,A2,A3,A4,A5,A6,A7,A8))
		{
			if(value.size() != 8)
			{
				Throw(rgcpp::IllegalArgumentException("Invalid number of arguments"));
			}

			A1 a1 = Deserializer<A1>::deserialize(value[0]);
			A2 a2 = Deserializer<A2>::deserialize(value[1]);
			A3 a3 = Deserializer<A3>::deserialize(value[2]);
			A4 a4 = Deserializer<A4>::deserialize(value[3]);
			A5 a5 = Deserializer<A5>::deserialize(value[4]);
			A6 a6 = Deserializer<A6>::deserialize(value[5]);
			A7 a7 = Deserializer<A7>::deserialize(value[6]);
			A8 a8 = Deserializer<A8>::deserialize(value[7]);

			return (*func)(a1,a2,a3,a4,a5,a6,a7,a8);
		}

		template<typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
		static std::vector<std::string> execute(std::vector<std::string> value, std::vector<std::string> (*func)(A1,A2,A3,A4,A5,A6,A7))
		{
			if(value.size() != 7)
			{
				Throw(rgcpp::IllegalArgumentException("Invalid number of arguments"));
			}

			A1 a1 = Deserializer<A1>::deserialize(value[0]);
			A2 a2 = Deserializer<A2>::deserialize(value[1]);
			A3 a3 = Deserializer<A3>::deserialize(value[2]);
			A4 a4 = Deserializer<A4>::deserialize(value[3]);
			A5 a5 = Deserializer<A5>::deserialize(value[4]);
			A6 a6 = Deserializer<A6>::deserialize(value[5]);
			A7 a7 = Deserializer<A7>::deserialize(value[6]);

			return (*func)(a1,a2,a3,a4,a5,a6,a7);
		}

		template<typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
		static std::vector<std::string> execute(std::vector<std::string> value, std::vector<std::string> (*func)(A1,A2,A3,A4,A5,A6))
		{
			if(value.size() != 6)
			{
				Throw(rgcpp::IllegalArgumentException("Invalid number of arguments"));
			}

			A1 a1 = Deserializer<A1>::deserialize(value[0]);
			A2 a2 = Deserializer<A2>::deserialize(value[1]);
			A3 a3 = Deserializer<A3>::deserialize(value[2]);
			A4 a4 = Deserializer<A4>::deserialize(value[3]);
			A5 a5 = Deserializer<A5>::deserialize(value[4]);
			A6 a6 = Deserializer<A6>::deserialize(value[5]);

			return (*func)(a1,a2,a3,a4,a5,a6);
		}

		template<typename A1, typename A2, typename A3, typename A4, typename A5>
		static std::vector<std::string> execute(std::vector<std::string> value, std::vector<std::string> (*func)(A1,A2,A3,A4,A5))
		{
			if(value.size() != 5)
			{
				Throw(rgcpp::IllegalArgumentException("Invalid number of arguments"));
			}

			A1 a1 = Deserializer<A1>::deserialize(value[0]);
			A2 a2 = Deserializer<A2>::deserialize(value[1]);
			A3 a3 = Deserializer<A3>::deserialize(value[2]);
			A4 a4 = Deserializer<A4>::deserialize(value[3]);
			A5 a5 = Deserializer<A5>::deserialize(value[4]);

			return (*func)(a1,a2,a3,a4,a5);
		}

		template<typename A1, typename A2, typename A3, typename A4>
		static std::vector<std::string> execute(std::vector<std::string> value, std::vector<std::string> (*func)(A1,A2,A3,A4))
		{
			if(value.size() != 4)
			{
				Throw(rgcpp::IllegalArgumentException("Invalid number of arguments"));
			}

			A1 a1 = Deserializer<A1>::deserialize(value[0]);
			A2 a2 = Deserializer<A2>::deserialize(value[1]);
			A3 a3 = Deserializer<A3>::deserialize(value[2]);
			A4 a4 = Deserializer<A4>::deserialize(value[3]);

			return (*func)(a1,a2,a3,a4);
		}

		template<typename A1, typename A2, typename A3>
		static std::vector<std::string> execute(std::vector<std::string> value, std::vector<std::string> (*func)(A1,A2,A3))
		{
			if(value.size() != 3)
			{
				Throw(rgcpp::IllegalArgumentException("Invalid number of arguments"));
			}

			A1 a1 = Deserializer<A1>::deserialize(value[0]);
			A2 a2 = Deserializer<A2>::deserialize(value[1]);
			A3 a3 = Deserializer<A3>::deserialize(value[2]);

			return (*func)(a1,a2,a3);
		}

		template<typename A1, typename A2>
		static std::vector<std::string> execute(std::vector<std::string> value, std::vector<std::string> (*func)(A1,A2))
		{
			if(value.size() != 2)
			{
				Throw(rgcpp::IllegalArgumentException("Invalid number of arguments"));
			}

			A1 a1 = Deserializer<A1>::deserialize(value[0]);
			A2 a2 = Deserializer<A2>::deserialize(value[1]);

			return (*func)(a1,a2);
		}

		template<typename A1>
		static std::vector<std::string> execute(std::vector<std::string> value, std::vector<std::string> (*func)(A1))
		{
			if(value.size() != 1)
			{
				Throw(rgcpp::IllegalArgumentException("Invalid number of arguments"));
			}

			A1 a1 = Deserializer<A1>::deserialize(value[0]);

			return (*func)(a1);
		}

		static std::vector<std::string> execute(std::vector<std::string> value, std::vector<std::string> (*func)())
		{
			if(value.size() != 0)
			{
				Throw(rgcpp::IllegalArgumentException("Invalid number of arguments"));
			}

			return (*func)();
		}
};

class Generator
{
	public:
		template<typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10>
		static std::string generateMethod(const std::string& className, const std::string& methodName, std::vector<std::string> (*func)(A1,A2,A3,A4,A5,A6,A7,A8,A9,A10))
		{
			return Generator::generateMethod(className,methodName,"value1,value2,value3,value4,value5,value6,value7,value8,value9,value10","param1='+value1+'&param2='+value2+'&param3='+value3+'&param4='+value4+'&param5='+value5+'&param6='+value6+'&param7='+value7+'&param8='+value8+'&param9='+value9+'&param10='+value10+'");
		}

		template<typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9>
		static std::string generateMethod(const std::string& className, const std::string& methodName, std::vector<std::string> (*func)(A1,A2,A3,A4,A5,A6,A7,A8,A9))
		{
			return Generator::generateMethod(className,methodName,"value1,value2,value3,value4,value5,value6,value7,value8,value9","param1='+value1+'&param2='+value2+'&param3='+value3+'&param4='+value4+'&param5='+value5+'&param6='+value6+'&param7='+value7+'&param8='+value8+'&param9='+value9+'");
		}

		template<typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
		static std::string generateMethod(const std::string& className, const std::string& methodName, std::vector<std::string> (*func)(A1,A2,A3,A4,A5,A6,A7,A8))
		{
			return Generator::generateMethod(className,methodName,"value1,value2,value3,value4,value5,value6,value7,value8","param1='+value1+'&param2='+value2+'&param3='+value3+'&param4='+value4+'&param5='+value5+'&param6='+value6+'&param7='+value7+'&param8='+value8+'");
		}

		template<typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
		static std::string generateMethod(const std::string& className, const std::string& methodName, std::vector<std::string> (*func)(A1,A2,A3,A4,A5,A6,A7))
		{
			return Generator::generateMethod(className,methodName,"value1,value2,value3,value4,value5,value6,value7","param1='+value1+'&param2='+value2+'&param3='+value3+'&param4='+value4+'&param5='+value5+'&param6='+value6+'&param7='+value7+'");
		}

		template<typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
		static std::string generateMethod(const std::string& className, const std::string& methodName, std::vector<std::string> (*func)(A1,A2,A3,A4,A5,A6))
		{
			return Generator::generateMethod(className,methodName,"value1,value2,value3,value4,value5,value6","param1='+value1+'&param2='+value2+'&param3='+value3+'&param4='+value4+'&param5='+value5+'&param6='+value6+'");
		}

		template<typename A1, typename A2, typename A3, typename A4, typename A5>
		static std::string generateMethod(const std::string& className, const std::string& methodName, std::vector<std::string> (*func)(A1,A2,A3,A4,A5))
		{
			return Generator::generateMethod(className,methodName,"value1,value2,value3,value4,value5","param1='+value1+'&param2='+value2+'&param3='+value3+'&param4='+value4+'&param5='+value5+'");
		}

		template<typename A1, typename A2, typename A3, typename A4>
		static std::string generateMethod(const std::string& className, const std::string& methodName, std::vector<std::string> (*func)(A1,A2,A3,A4))
		{
			return Generator::generateMethod(className,methodName,"value1,value2,value3,value4","param1='+value1+'&param2='+value2+'&param3='+value3+'&param4='+value4+'");
		}

		template<typename A1, typename A2, typename A3>
		static std::string generateMethod(const std::string& className, const std::string& methodName, std::vector<std::string> (*func)(A1,A2,A3))
		{
			return Generator::generateMethod(className,methodName,"value1,value2,value3","param1='+value1+'&param2='+value2+'&param3='+value3+'");
		}

		template<typename A1, typename A2>
		static std::string generateMethod(const std::string& className, const std::string& methodName, std::vector<std::string> (*func)(A1,A2))
		{
			return Generator::generateMethod(className,methodName,"value1,value2","param1='+value1+'&param2='+value2+'");
		}

		template<typename A1>
		static std::string generateMethod(const std::string& className, const std::string& methodName, std::vector<std::string> (*func)(A1))
		{
			return Generator::generateMethod(className,methodName,"value1","param1='+value1+'");
		}

		static std::string generateMethod(const std::string& className, const std::string& methodName, std::vector<std::string> (*func)())
		{
			return Generator::generateMethod(className,methodName,"","");
		}

		static std::string generateClass(const std::string& className)
		{
			return std::string("function ")+className+std::string("()\n{\n}\n\n");
		}

		static std::string generateMethod(const std::string& className, const std::string& methodName, const std::string& parameters, const std::string& values)
		{
			std::stringstream stream;
			if(parameters.empty())
				stream << className << "." << methodName << " = function(myFunc,myScope) \n";
			else
				stream << className << "." << methodName << " = function(" << parameters << ",myFunc,myScope) \n";
			stream << "{\n";
			stream << "	Ext.Ajax.request({\n";
			stream << "		url: 'JavaScriptInterface',\n";
			if(values.empty())
				stream << "		params: 'method=" << className << "." << methodName << "',\n";
			else
				stream << "		params: 'method=" << className << "." << methodName << "&" << values << "',\n";
			stream << "		success: function(transport)\n";
			stream << "			{\n";
			stream << "				if(myFunc)\n";
			stream << "				{\n";
			stream << "					myFunc(myScope,eval(transport.responseText));\n";
			stream << "				}\n";
			stream << "			},\n";
			stream << "		failure: function(transport) {alert(transport.responseText);}\n";
			stream << "	});\n";
			stream << "}\n\n";

			return stream.str();
		}
};

class Invoker
{
	protected:
		static rgcpp::Set<std::string> setClassList;

	public:
		virtual std::vector<std::string> call(std::vector<std::string> arguments) = 0;
		virtual std::string generateJavaScript() = 0;
};

template<typename FuncType>
class InvokerInterface : public Invoker
{
	private:
		FuncType func;
		std::string className;
		std::string methodName;

	public:
		InvokerInterface(FuncType func, const std::string& className, const std::string& methodName)
		{
			this->func = func;
			this->className = className;
			this->methodName = methodName;
		}

		virtual ~InvokerInterface()
		{

		}

		std::vector<std::string> call(std::vector<std::string> arguments)
		{
			return Executor::execute(arguments,this->func);
		}

		std::string generateJavaScript()
		{
			std::string result;

			if(this->setClassList.find(this->className) == this->setClassList.end())
			{
				result += Generator::generateClass(this->className);
				this->setClassList.insert(this->className);
			}

			result += Generator::generateMethod(this->className,this->methodName,this->func);

			return result;
		}
};

}

#endif
